package junit;

import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;
import java.security.InvalidKeyException;
import java.security.Key;
import java.security.KeyFactory;
import java.security.KeyPair;
import java.security.KeyPairGenerator;
import java.security.NoSuchAlgorithmException;
import java.security.PrivateKey;
import java.security.PublicKey;
import java.security.SecureRandom;
import java.security.interfaces.RSAPrivateKey;
import java.security.interfaces.RSAPublicKey;
import java.security.spec.InvalidKeySpecException;
import java.security.spec.PKCS8EncodedKeySpec;
import java.security.spec.X509EncodedKeySpec;

import javax.crypto.BadPaddingException;
import javax.crypto.Cipher;
import javax.crypto.IllegalBlockSizeException;
import javax.crypto.NoSuchPaddingException;

import org.springframework.util.Base64Utils;

/**
 * @author suns suntion@yeah.net
 * @since 2017年4月6日下午3:11:46
 */
public final class RsaEncrypt {

    /**
     * 工具类私有化构造器
     */
    private RsaEncrypt() {
    }

    /**
     * KeyPairGenerator 生成密钥
     * 
     * @param publicKeyFilePath
     *            公钥存储地址
     * @param privateKeyFilePath
     *            私钥存储地址
     * @throws NoSuchAlgorithmException 异常
     * @throws IOException io异常
     */
    public static void generatorKeyPair(String publicKeyFilePath, String privateKeyFilePath) throws NoSuchAlgorithmException, IOException {
        KeyPairGenerator keyPairGen = KeyPairGenerator.getInstance("RSA");

        keyPairGen.initialize(1024, new SecureRandom());

        KeyPair keyPair = keyPairGen.generateKeyPair();

        PrivateKey privateKey = keyPair.getPrivate();
        PublicKey publicKey = keyPair.getPublic();

        String publicString = Base64Utils.encodeToString(publicKey.getEncoded());
        String privateString = Base64Utils.encodeToString(privateKey.getEncoded());

        FileWriter pubfw = new FileWriter(publicKeyFilePath);
        BufferedWriter pubbw = new BufferedWriter(pubfw);
        pubbw.write(publicString);
        pubbw.flush();
        pubbw.close();
        pubfw.close();

        FileWriter prifw = new FileWriter(privateKeyFilePath);
        BufferedWriter pribw = new BufferedWriter(prifw);
        pribw.write(privateString);
        pribw.flush();
        pribw.close();
        prifw.close();
    }

    /**
     * 根据文件地址 读取
     * 
     * @param filePath
     *            文件地址
     * @return 密钥
     * @throws Exception 异常
     */
    public static String loadByFile(String filePath) throws Exception {
        try {
            BufferedReader br = new BufferedReader(new FileReader(filePath));
            String readLine = null;
            StringBuilder sb = new StringBuilder();
            while ((readLine = br.readLine()) != null) {
                sb.append(readLine);
            }
            br.close();
            return sb.toString();
        } catch (IOException e) {
            throw new Exception("数据流读取错误");
        } catch (NullPointerException e) {
            throw new Exception("输入流为空");
        }
    }

    /**
     * 根据字符串公钥生成 RSAPublicKey
     * 
     * @param publicKeyStr
     *            公钥字符串
     * @return RSA公钥
     * @throws Exception 异常
     */
    public static RSAPublicKey convertPublicKeyByStr(String publicKeyStr) throws Exception {
        try {
            byte[] buffer = Base64Utils.decodeFromString(publicKeyStr);
            KeyFactory keyFactory = KeyFactory.getInstance("RSA");
            X509EncodedKeySpec keySpec = new X509EncodedKeySpec(buffer);
            return (RSAPublicKey) keyFactory.generatePublic(keySpec);
        } catch (NoSuchAlgorithmException e) {
            throw new Exception("无此算法");
        } catch (InvalidKeySpecException e) {
            throw new Exception("公钥非法");
        } catch (NullPointerException e) {
            throw new Exception("公钥数据为空");
        }
    }

    /**
     * 根据私钥字符串生成RSAPrivateKey
     * 
     * @param privateKeyStr
     *            私钥字符串
     * @return RSA私钥
     * @throws Exception 
     */
    public static RSAPrivateKey convertPrivateKeyByStr(String privateKeyStr) throws Exception {
        try {
            byte[] buffer = Base64Utils.decodeFromString(privateKeyStr);
            PKCS8EncodedKeySpec keySpec = new PKCS8EncodedKeySpec(buffer);
            KeyFactory keyFactory = KeyFactory.getInstance("RSA");
            return (RSAPrivateKey) keyFactory.generatePrivate(keySpec);
        } catch (NoSuchAlgorithmException e) {
            throw new Exception("无此算法");
        } catch (InvalidKeySpecException e) {
            throw new Exception("私钥非法");
        } catch (NullPointerException e) {
            throw new Exception("私钥数据为空");
        }
    }

    /**
     * 加密
     * 
     * @param key
     *            加密密钥
     * @param plainTextData
     *            数据
     * @return 加密后数据
     * @throws Exception
     *             异常
     */
    public static byte[] encrypt(Key key, byte[] plainTextData) throws Exception {
        try {
            if (key == null) {
                throw new Exception("密钥为空, 请设置");
            }
            Cipher cipher = Cipher.getInstance("RSA");
            cipher.init(Cipher.ENCRYPT_MODE, key);
            byte[] output = cipher.doFinal(plainTextData);
            return output;
        } catch (NoSuchAlgorithmException e) {
            throw new Exception("无此加密算法");
        } catch (NoSuchPaddingException e) {
            throw new Exception();
        } catch (InvalidKeyException e) {
            throw new Exception("加密公钥非法,请检查");
        } catch (IllegalBlockSizeException e) {
            throw new Exception("明文长度非法");
        } catch (BadPaddingException e) {
            throw new Exception("明文数据已损坏");
        }
    }

    /**
     * 解密
     * 
     * @param key
     *            密钥
     * @param decryptData
     *            解密数据
     * @return 解密后数据
     * @throws Exception
     *             异常
     */
    public static byte[] decrypt(Key key, byte[] decryptData) throws Exception {
        try {
            if (key == null) {
                throw new Exception("密钥为空, 请设置");
            }
            Cipher cipher = Cipher.getInstance("RSA");
            cipher.init(Cipher.DECRYPT_MODE, key);
            byte[] output = cipher.doFinal(decryptData);
            return output;
        } catch (NoSuchAlgorithmException e) {
            throw new Exception("无此解密算法");
        } catch (NoSuchPaddingException e) {
            throw new Exception();
        } catch (InvalidKeyException e) {
            throw new Exception("解密公钥非法,请检查");
        } catch (IllegalBlockSizeException e) {
            throw new Exception("密文长度非法");
        } catch (BadPaddingException e) {
            throw new Exception("密文数据已损坏");
        }
    }

}
